//
// Created by liaohailong on 2025/4/9.
//

#include "HelloFile.hpp"


HelloFile::HelloFile(const std::string &filepath) : logger("HelloFile"), filepath(filepath)
{
    std::filesystem::path path(filepath);
    filename = path.filename().string();  // 从路径中提取文件名
    logger.i("HelloFile::HelloFile(%p): %s", this, filepath.c_str());
}

HelloFile::HelloFile(const std::string &parent, const std::string &filename) : logger("HelloFile"),
                                                                               filename(filename)
{
    std::filesystem::path path = std::filesystem::path(parent) / filename;
    filepath = path.string();

    logger.i("HelloFile::HelloFile(%p): parent[%s]+filename[%s]=%s",
             this, parent.c_str(), filename.c_str(), filepath.c_str());
}

HelloFile::~HelloFile()
{
    logger.i("HelloFile::~HelloFile(%p): %s", this, filepath.c_str());
}


bool HelloFile::exist()
{
    std::filesystem::path path(filepath);
    return std::filesystem::exists(path);
}

/**
 * 判断 filepath 是否为目录（存在的）
 * @param filepath 全路径
 * @return true=是目录
 */
bool HelloFile::isDir()
{
    std::filesystem::path path(filepath);
    return std::filesystem::is_directory(path);  // 使用 std::filesystem 判断是否是目录
}

/**
 * 判断 filepath 是否为文件（存在的）
 * @param filepath 全路径
 * @return true=是文件
 */
bool HelloFile::isFile()
{
    std::filesystem::path path(filepath);
    return std::filesystem::is_regular_file(path);  // 使用 std::filesystem 判断是否是文件
}

std::vector<std::shared_ptr<HelloFile>> HelloFile::listFile()
{
    std::vector<std::shared_ptr<HelloFile>> list;
    namespace fs = std::filesystem;
    try
    {
        for (const fs::directory_entry &entry: fs::directory_iterator(filepath))
        {
            // 普通文件
            if (entry.is_regular_file())
            {
                std::shared_ptr<HelloFile> file = std::make_shared<HelloFile>(entry.path());
                list.emplace_back(file);
            }
                // 目录文件
            else if (entry.is_directory())
            {
                std::shared_ptr<HelloFile> file = std::make_shared<HelloFile>(entry.path());
                list.emplace_back(file);
            }
            // 其他的不添加了 FIFO SOCKET 软连接 等等...
        }
    } catch (const fs::filesystem_error &e)
    {
        logger.e("Filesystem error: %s", e.what());
    }

    return list;
}

/**
 * @return 创建一个普通文件，如果当前文件存在，则会失败
 */
bool HelloFile::createNewFile()
{
    namespace fs = std::filesystem;
    if (fs::exists(filepath))
    {
        logger.i("File already exists: %s", filepath.c_str());
        return false;
    }

    std::ofstream ofs(filepath, std::ios::out | std::ios::binary);
    if (!ofs)
    {
        logger.e("Failed to create file: %s", filepath.c_str());
        return false;
    }

    logger.i("File created: %s", filepath.c_str());
    return true;
}

/**
 * @return 逐级创建目录，当前目录存在，则会失败
 */
bool HelloFile::mkdirs()
{
    namespace fs = std::filesystem;
    if (fs::exists(filepath))
    {
        logger.i("Directory already exists: %s", filepath.c_str());
        return false;
    }

    try
    {
        if (fs::create_directories(filepath))
        {
            logger.i("Directories created: %s", filepath.c_str());
            return true;
        } else
        {
            logger.e("Failed to create directories (unknown reason): %s", filepath.c_str());
            return false;
        }
    } catch (const fs::filesystem_error &e)
    {
        logger.e("Filesystem error while creating directories: %s", e.what());
        return false;
    }
}

bool HelloFile::deleteFile()
{
    namespace fs = std::filesystem;

    if (!fs::exists(filepath))
    {
        logger.e("File or directory does not exist: %s", filepath.c_str());
        return false;
    }

    try
    {
        std::uintmax_t count = 0;
        if (fs::is_directory(filepath))
        {
            count = fs::remove_all(filepath);  // 删除整个目录及其内容
            logger.i("Directory deleted (%zu items): %s", count, filepath.c_str());
        } else
        {
            if (fs::remove(filepath))
            {
                logger.i("File deleted: %s", filepath.c_str());
                count = 1;
            } else
            {
                logger.e("Failed to delete file: %s", filepath.c_str());
                return false;
            }
        }
        return count > 0;
    }
    catch (const fs::filesystem_error &e)
    {
        logger.e("Filesystem error while deleting: %s", e.what());
        return false;
    }
}

/**
 * @return 返回上一级目录(全路径)，没有的话返回空字符串
 */
std::string HelloFile::getParent()
{
    namespace fs = std::filesystem;
    fs::path currentPath(filepath);
    fs::path parentPath = currentPath.parent_path();

    if (parentPath.empty() || parentPath == currentPath)
    {
        logger.e("No parent found for: %s", filepath.c_str());
        return "";
    }

    return parentPath.string();
}

/**
 * @return 获取完全的全路径
 */
std::string HelloFile::getAbsolutFilepath()
{
    return filepath;
}

/**
 * @return 获取文件名
 */
std::string HelloFile::getFilename()
{
    return filename;
}

/**
 * @return 获取文件大小（单位：字节），如果不是普通文件或获取失败，返回 -1
 */
int64_t HelloFile::getLength()
{
    namespace fs = std::filesystem;

    try
    {
        if (fs::is_regular_file(filepath))
        {
            return static_cast<int64_t>(fs::file_size(filepath));
        } else
        {
            logger.e("Not a regular file: %s", filepath.c_str());
        }
    }
    catch (const fs::filesystem_error &e)
    {
        logger.e("Filesystem error while getting file size: %s", e.what());
    }

    return -1;
}

std::string HelloFile::readFileAsString()
{
    std::ifstream ifs(filepath);
    if (!ifs.is_open())
    {
        // 打开失败
        return "";
    }

    std::ostringstream oss;
    oss << ifs.rdbuf();  // 读取整个文件缓冲区
    return oss.str();
}

/**
 * 往文本中写入字符串
 * @param content 待写入的字符串
 * @param append true表示追加式写入
 * @return true表示写入成功
 */
bool HelloFile::writeString2File(const std::string &content, bool append)
{
    std::ios_base::openmode mode = std::ios::out;

    if (append)
    {
        mode |= std::ios::app; // 追加写入
    } else
    {
        mode |= std::ios::trunc; // 覆盖写入
    }

    std::ofstream ofs(filepath, mode);
    if (!ofs)
    {
        logger.e("Failed to open file for writing: %s", filepath.c_str());
        return false;
    }

    ofs << content;
    if (!ofs.good())
    {
        logger.e("Failed to write content to file: %s", filepath.c_str());
        return false;
    }

    logger.i("Successfully wrote content to file: %s", filepath.c_str());
    return true;
}

/**
 * 以二进制流读取文件数据
 * @param offset 起始偏移
 * @param length 一共读多少字节量
 * @param read 分片读取回调
 * @return true表示全部成功读完，false表示没读全
 */
bool HelloFile::readFileBinary(std::streamoff offset, std::streamsize length,
                               const OnChunkRead &read)
{
    std::ifstream ifs(filepath, std::ios::binary);
    if (!ifs) return false;

    // 设置起始偏移
    if (offset > 0)
    {
        ifs.seekg(offset, std::ios::beg);
        if (!ifs) return false;  // 检查 seek 是否成功
    }

    bool success = true;
    char fileBuffer[8192];
    std::streamsize totalRead = 0;

    while (ifs && totalRead < length)
    {
        std::streamsize toRead = std::min<std::streamsize>(sizeof(fileBuffer), length - totalRead);
        ifs.read(fileBuffer, toRead);
        std::streamsize bytes = ifs.gcount();
        if (bytes <= 0) break;

        totalRead += bytes;

        if (!read(fileBuffer, bytes))
        {
            success = false;
            break;
        }
    }

    return success && (totalRead == length);
}

/**
 * 追加（拼接）文件
 * @param file 待追加（拼接）的文件
 * @param offset 从offset位置（包含）开始写入字节
 * @return true表示成功追加（拼接）
 */
bool HelloFile::appendFile(const std::shared_ptr<HelloFile> &file, int64_t offset)
{
    if (!file || !file->isFile())
    {
        logger.e("Invalid file to append: null or not a regular file");
        return false;
    }

    std::ifstream in(file->getAbsolutFilepath(), std::ios::binary);
    if (!in.is_open())
    {
        logger.e("Failed to open input file: %s", file->getAbsolutFilepath().c_str());
        return false;
    }

    // 注意：不要用 std::ios::app（它会强制所有写入跳到末尾）
    std::ofstream out(filepath, std::ios::binary | std::ios::in | std::ios::out);
    if (!out.is_open())
    {
        logger.e("Failed to open target file for writing: %s", filepath.c_str());
        return false;
    }

    // 如果 offset < 0，则写到末尾
    if (offset < 0)
    {
        out.seekp(0, std::ios::end);
    }
    else
    {
        out.seekp(offset, std::ios::beg);
    }

    if (!out)
    {
        logger.e("Failed to seek output stream to position: %ld", offset);
        return false;
    }

    char buffer[8192];
    while (in)
    {
        in.read(buffer, sizeof(buffer));
        std::streamsize bytesRead = in.gcount();
        if (bytesRead > 0)
        {
            out.write(buffer, bytesRead);
            if (!out)
            {
                logger.e("Write failed while appending from %s",
                         file->getAbsolutFilepath().c_str());
                return false;
            }
        }
    }

    return true;
}